//
//  CAIFileCache.m
//  Runner
//
//  Created by simon on 2021/10/19.
//
/// 不适合记录存储方式，因为需要本地记录；导致又得读属性列表来判断；和现在优先用属性列表判断一样效果；

#import "CAIFileCache.h"
#import <TMCache/TMCache.h>
#import "NSData+CAIStorageBase64.h"

static CGFloat const kLimitPropertyListStoreMax  = 150;

static NSString *const kCAIDBCacheInfoMapKey =  @"kCAIDBCacheInfoMapKey";

#ifndef WS
#define WS(weakSelf)  __weak __typeof(&*self)weakSelf = self;
#endif

///缓存类型
typedef NS_ENUM(NSUInteger, CAIDBManagerCacheType)
{
    ///缓存类型UserDefault
    CAIDBManagerCacheTypePList = 0,
    ///缓存类型Archive
    CAIDBManagerCacheTypeArchive = 1,
};


@interface CAIFileCache ()

@property (nonatomic, strong) NSMutableDictionary *cacheInfoMDic;

@property (nonatomic, strong) TMCache *fileStorage;

@end

@implementation CAIFileCache


+ (instancetype)sharedCache
{
    static id instance = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        instance = [[self alloc] initWithName:@""];
    });
    return instance;
}

- (instancetype)init{
    self = [super init];
    if (self) {
        id obj2 = [[NSUserDefaults standardUserDefaults]objectForKey:kCAIDBCacheInfoMapKey];
        _cacheInfoMDic =  [NSMutableDictionary dictionaryWithDictionary:obj2];
        [CAIFileCache setVersion:1];
    }
    return self;
}

- (instancetype)initWithName:(nullable NSString *)name
{
    return [self initWithName:name rootPath:[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0]];
}

- (instancetype)initWithName:(nullable NSString *)name rootPath:(NSString *)rootPath
{
    if (!name || name.length == 0)
    {
        name = @"TMDiskCacheShared";
    }
    if (self = [super init]) {
        _fileStorage = [[TMCache alloc] initWithName:name rootPath:rootPath];
    }
    return self;
}

- (void)setObject:(id)object forKey:(NSString *)key
{
    if ([self.cacheInfoMDic.allKeys containsObject:key]) {
        NSDictionary *infoDic  = [self.cacheInfoMDic objectForKey:key];
        NSString *type = [infoDic objectForKey:@"type"];
        NSString *exceptionStr = [NSString stringWithFormat:@"不能存archiveObject:方法已经使用的key:%@",key];
        NSAssert([type isEqualToString:@"plist"], exceptionStr);
    }
    [self saveCacheInfoWithKey:key withVersion:[CAIFileCache version] type:CAIDBManagerCacheTypePList];
    if ([object isKindOfClass:[NSNumber class]]||
        [object isKindOfClass:[NSDate class]]) {
        [[NSUserDefaults standardUserDefaults]setObject:object forKey:key];
        return;
    }
    [self sizeOfData:object completion:^(CGFloat length, NSError * _Nullable error) {
        if (!error) {
            //兜底处理，遇到歧义大的数据会存储archive中；不建议依赖此方法
            if (length/1024 > kLimitPropertyListStoreMax) {
                
                if ([[NSUserDefaults standardUserDefaults]objectForKey:key]) {
                    [[NSUserDefaults standardUserDefaults]removeObjectForKey:key];
                }
                [self.fileStorage setObject:object forKey:key];
                NSLog(@"不建议此方法存储大数据");
            }else{
                [[NSUserDefaults standardUserDefaults]setObject:object forKey:key];
            }
        }else{
            NSAssert(!error, @"CAIDBManager报错信息 - %@", error);
        }
    }];
}


- (void)setEncodeCryptObject:(id)object
                      forKey:(NSString *)key
                       error:(NSError * __autoreleasing *)error;
{
    NSData *data = [NSData base64EncodedData:object error:error];
    [self setObject:data forKey:key];
}


- (void)archiveObject:(id <NSCoding>)object forKey:(NSString *)key
{
    NSString *exceptionStr = [NSString stringWithFormat:@"不能存NSUserDefault方法已经使用的key:%@",key];
    NSAssert(![[NSUserDefaults standardUserDefaults]objectForKey:key], exceptionStr);
    [self saveCacheInfoWithKey:key withVersion:[CAIFileCache version] type:CAIDBManagerCacheTypeArchive];
    [self.fileStorage setObject:object forKey:key];
}


- (void)archiveEncodeCryptObject:(id <NSSecureCoding>)object
                          forKey:(NSString *)key
                           error:(NSError * __autoreleasing *)error
{
    NSData *data = [NSData base64EncodedData:object error:error];
    [self archiveObject:data forKey:key];
}

#pragma mark - 统一取数据

- (id)objectForKey:(NSString *)key
{
    if ([[NSUserDefaults standardUserDefaults]objectForKey:key]) {
        return  [[NSUserDefaults standardUserDefaults]objectForKey:key];
    }
    return [self.fileStorage objectForKey:key];
}


- (id)objectDecodeCryptForKey:(NSString *)key
                expectedClass:(Class)cls
                        error:(NSError * __autoreleasing *)error
{
    id obj =  [self objectForKey:key];
    if ([obj isKindOfClass:[NSData class]]) {
         NSData *base64Data =(NSData *)obj;
         id decodedObj = [NSData base64DecodedData:base64Data class:cls error:error];
         return decodedObj;
    }
    return obj;
}

#pragma mark - 统一删除数据

- (void)removeObjectForKey:(NSString *)key
{
    if ([[NSUserDefaults standardUserDefaults]objectForKey:key]) {
        [self removeCacheInfoWithKey:key withVersion:[CAIFileCache version] type:CAIDBManagerCacheTypePList];
        [[NSUserDefaults standardUserDefaults]removeObjectForKey:key];
    }else{
        [self removeCacheInfoWithKey:key withVersion:[CAIFileCache version] type:CAIDBManagerCacheTypeArchive];
        [self.fileStorage removeObjectForKey:key];
    }
}


#pragma mark - 计算大小

- (void)sizeOfData:(id)object completion:(void(^)(CGFloat length, NSError  * _Nullable error))completion
{
    if (@available(iOS 11.0, *)) {
        NSError *error;
        NSData *data = [NSKeyedArchiver archivedDataWithRootObject:object requiringSecureCoding:YES error:&error];
        if (error) {
            completion(0, error);
        }else{
            completion((CGFloat)data.length, nil);
        }
    } else {
        NSData *data = [NSKeyedArchiver archivedDataWithRootObject:object];
        completion((CGFloat)data.length, nil);
    }
}


- (void)saveCacheInfoWithKey:(NSString *)key
                 withVersion:(NSInteger)version
                        type:(CAIDBManagerCacheType)type
{
    NSMutableDictionary *valueDic = [NSMutableDictionary dictionary];
    [valueDic setObject:@(version) forKey:@"v"];
    if (type == CAIDBManagerCacheTypePList) {
        [valueDic setObject:@"plist" forKey:@"type"];
    }else{
        [valueDic setObject:@"archive" forKey:@"type"];
    }
    [self.cacheInfoMDic setObject:valueDic forKey:key];
    [[NSUserDefaults standardUserDefaults]setObject:self.cacheInfoMDic forKey:kCAIDBCacheInfoMapKey];
}

- (void)removeCacheInfoWithKey:(NSString *)key
                   withVersion:(NSInteger)version
                          type:(NSInteger)type
{
    if ([self.cacheInfoMDic.allKeys containsObject:key]) {
        [self.cacheInfoMDic removeObjectForKey:key];
        [[NSUserDefaults standardUserDefaults]setObject:self.cacheInfoMDic forKey:kCAIDBCacheInfoMapKey];
    }
}


@end
